package com.zxq.common.middleware.lock;


import com.zxq.common.middleware.gateway.RedisLockGateway;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

@Getter
@Setter
@Slf4j
public class RedisProcessor {

    /**
     * Redis 客户端
     */
    @Getter(AccessLevel.PACKAGE)
    private RedisLockGateway redisLockGateway;

    /**
     * 存储类别, 用于指定某一类存储数据, 可以类比的理解为数据库中的一张表, 具有统一的过期时间, 统一由框架设置, 无需用户进一步的设置;
     * 不同的 category 名称作为 key 前缀来进行区分使用同一个 Redis 集群不同的业务方;
     */
    private String category = "distributed_lock";

    public RedisProcessor(RedisLockGateway redisLockGateway) throws Exception {
        this.redisLockGateway = redisLockGateway;
    }

    public boolean setNx(StoreKey key, String value, int leaseTime, int retry) throws Exception {

        boolean result;
        try {
            result = redisLockGateway.setNx(key, value, leaseTime);
        } catch (Exception e) {
            result = exceptionHandler(RedisMethodEnum.SET_NX, e, key, value, null, leaseTime, retry);
            if (!result) {
                // 有可能客户端超时，但服务端已成功添加（key，value）
                if (redisLockGateway.exists(key)) {
                    // 若key存在，获取value1，并判断value1是否与value相等，相等则返回true
                    String getValue = redisLockGateway.get(key);

                    return StringUtils.equals(value, getValue);
                }
            }
        }

        return result;
    }

    public boolean compareAndDelete(StoreKey key, String value, int retry) throws Exception {

        Boolean result;
        try {
            result = redisLockGateway.compareAndDelete(key, value);
        } catch (Exception e) {
            result = exceptionHandler(RedisMethodEnum.COMPARE_AND_DELETE, e, key, value, null, -1, retry);
        }

        return result;

    }

    public boolean compareAndSet(StoreKey key, String oldValue, String newValue, int expireTime, int retry) throws Exception {

        Boolean result;
        try {
            result = redisLockGateway.compareAndSet(key, oldValue, newValue, expireTime);
        } catch (Exception e) {
            result = exceptionHandler(RedisMethodEnum.COMPARE_AND_DELETE, e, key, oldValue, newValue, expireTime, retry);
        }

        return result;

    }

    /**
     * 处理redis的操作抛出的异常，根据重试次数进行回调
     *
     * @param type
     * @param RedisException
     * @param key
     * @param oldValue
     * @param newValue
     * @param expireTime
     * @param retry
     * @return
     * @throws Exception
     */
    private boolean exceptionHandler(
            RedisMethodEnum type, Exception redisException,
            StoreKey key, String oldValue, String newValue, int expireTime, int retry) throws Exception {
        if (Thread.interrupted())
            throw new InterruptedException();

        log.error("redis error, lockName:{}", key.toString());
        boolean result = false;
        if (retry > 0) {
            retry--;
            Thread.sleep(100);
            log.info(type.getField() + " error; Retry:" + (retry + 1) + "; Key:" + key + "; exception:" + redisException.toString());
            switch (type) {
                case SET_NX:
                    log.info("add retry");
                    result = this.setNx(key, oldValue, expireTime, retry);
                    break;
                case COMPARE_AND_DELETE:
                    log.info("compare_and_delete retry");
                    result = this.compareAndDelete(key, oldValue, retry);
                    break;
                case COMPARE_AND_SET:
                    log.info("compare_and_set retry");
                    result = this.compareAndSet(key, oldValue, newValue, expireTime, retry);
                    break;
                default:
                    break;
            }
        } else {
            log.error(type.getField() + " error; Key:" + key, redisException);
            throw redisException;
        }

        return result;
    }
}
